package client;

import java.util.*;

import msgs.*;
import msgs.TTConstants.Actions;

import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;

/**
 * MsgParser: Parses incoming messages from the server
 * Team 9: Andrew Hayworth, Brian Parrella, Ryan Kortmann, Nina Papa
 * @author Andrew Hayworth
 */

public class MsgParser extends Thread implements TTConstants {
	private ClientMsg cmsg;
	private static boolean go;
	
	// this is lazy gui coding ... but I'm a lazy guy. 
	private JDialog jd;
	private JTextArea jta;
	
	public MsgParser() {
		go = true;
	}
	
	public void run() {
		while (go) {
			// only process if there's something to process
			if(!(ServerListener.inQ.isEmpty())){
				cmsg = ServerListener.inQ.remove(0);
				System.out.println(cmsg.getAction());
				switch (cmsg.getAction()) {
				
				case ADMIN_DISCONNECT:
					System.out.println("Disco msg recvd");
					// shit, we gotta gtfo!
					System.exit(0);
					break;
				case ADMIN_LIST_CHATS:
					ArrayList<String> a = new ArrayList<String>();
					a.addAll(TTClient.pmChats.keySet());
					String[] as = makeStringArray(a);
					cmsg.setChats(as);
					ServerSender.outQ.add(cmsg);
					break;
				// msgs sent to and from the server with action OFFLINE_MSG are offline PM messages, and responses from the server
				case OFFLINE_MSG:
					if (cmsg instanceof PMMsg) {
						// we only actually care if they came back online. the server sneds OK for every new message we send,
						//but I dont actually care if it fails, so i never test for that.
						if (((PMMsg)cmsg).getSpecialResponse() == 3 ) {
							// they came back online, so we change the chat with their name to reflect this and resend the last message requested
							TTClient.pmChats.get(((PMMsg)cmsg).getWantsUser()).setSpecialResponse(0);
							TTClient.pmChats.get(((PMMsg)cmsg).getWantsUser()).setTargetIP(((PMMsg)cmsg).getUserIP());
							PMSender.outQ.add(new SimpleMsg(((PMMsg)cmsg).getMsg(), TTClient.name, TTClient.pmChats.get(((PMMsg)cmsg).getWantsUser()).getTargetName(), TTClient.pmChats.get(((PMMsg)cmsg).getWantsUser()).getTargetIP(), TTClient.pmChats.get(((PMMsg)cmsg).getWantsUser()).getFromIP(), false));
							break;
						}
					}
					if (cmsg instanceof OfflinePMMsg) {
						/// HERE BE HACKS!!! DONT TOUCH!!!
						// sweet, someone needed to talk to us so badly that they left us a message!
						// this is the case that we already have a window open for them
						if (TTClient.pmChats.containsKey(((OfflinePMMsg)cmsg).getFromUser())) {
							TTClient.pmChats.get(((OfflinePMMsg)cmsg).getFromUser()).setVisible(true);
							TTClient.pmChats.get(((OfflinePMMsg)cmsg).getFromUser()).updateWindow(((OfflinePMMsg)cmsg).getMsg());
							TTClient.pmChats.get(((OfflinePMMsg)cmsg).getFromUser()).setSpecialResponse(2);
						}
						else {
							// we dont have a window open for them yet, create one!
							TTClient.pmChats.put(((OfflinePMMsg)cmsg).getFromUser(), new PMChat(new PMMsg(TTClient.ip, TTClient.name, null, TTClient.key, ((OfflinePMMsg)cmsg).getFromUser())));
							TTClient.pmChats.get(((OfflinePMMsg)cmsg).getFromUser()).updateWindow("**** Messages sent to you while offline ****");
							TTClient.pmChats.get(((OfflinePMMsg)cmsg).getFromUser()).updateWindow(((OfflinePMMsg)cmsg).getMsg());
							TTClient.pmChats.get(((OfflinePMMsg)cmsg).getFromUser()).setFromIP(((OfflinePMMsg)cmsg).getIp());
							TTClient.pmChats.get(((OfflinePMMsg)cmsg).getFromUser()).setSpecialResponse(2);
						} 
					}
					break;
					// we tried to pm a user, check the response from the server to see if we're allowed
				case PM_USER:
					// not allowed to pm -- ie, we're blocked
					if (((PMMsg)cmsg).getSpecialResponse() == 1) {
						JOptionPane.showMessageDialog(null, "Msg Failed!", "Not allowed to PM this user", JOptionPane.ERROR_MESSAGE);
						break;
					}
					// allowed to pm, but they're offline
					// send to the server instead
					else if (((PMMsg)cmsg).getSpecialResponse() == 2) {		
						// we should create a new chat, and instead of simplemsgs , we send pmmsgs to the server
						if (!(TTClient.pmChats.containsKey(((PMMsg)cmsg).getWantsUser()))) {
							//they have no window open for this user yet
							JOptionPane.showMessageDialog(null, "Requested user is offline -- all messages will be stored and sent to the user when they log on.", "User offline!", JOptionPane.INFORMATION_MESSAGE);
							TTClient.pmChats.put(((PMMsg)cmsg).getWantsUser(), new PMChat((PMMsg) cmsg));
							TTClient.pmChats.get(((PMMsg)cmsg).getWantsUser()).setSpecialResponse(2);
						}
						else
						{
							// have a window, raise it.
							TTClient.pmChats.get(((PMMsg)cmsg).getWantsUser()).setVisible(true);
							TTClient.pmChats.get(((PMMsg)cmsg).getWantsUser()).setSpecialResponse(2);
						}
						break;
					}
					//yay, we're allowed to pm and they're online!
					else {
						if(!(TTRunningClient.jf.isVisible()))
						{
						//	TTClient.trayIcon.displayMessage("Message from: " + ((PMMsg)cmsg).getWantsUser(), ((PMMsg)cmsg).getMsg(), org.jdesktop.jdic.tray.TrayIcon.INFO_MESSAGE_TYPE);
						}
						
						if (!(TTClient.pmChats.containsKey(((PMMsg)cmsg).getWantsUser()))) {
							TTClient.pmChats.put(((PMMsg)cmsg).getWantsUser(), new PMChat((PMMsg) cmsg));
						}
						else
						{
							TTClient.pmChats.get(((PMMsg)cmsg).getWantsUser()).setVisible(true);
						}
						break;
					}
				case KEEP_ALIVE:
					// server sends periodic keepalives. it just expects a reply, it doesnt care what it is, just resend the last thing we got in.
					ServerSender.outQ.add(cmsg);
					break;
				case PROFILE_UPDATE:
					// we tried to update our profile. success == silent, failure == this case:
					if (cmsg.getResponse() == TTConstants.Responses.DENY) {
						JOptionPane.showMessageDialog(null, "Update Failed!", "Update Failed!", JOptionPane.ERROR_MESSAGE);
					}
					break;
				case PROFILE_REQUEST:
					// we either wanted to update our profile, or are looking at someone elses.
					// if we're currently updating our profile, display the editor window
					if (TTClient.updatingProfile) {
						TTClient.updatingProfile = false;
						jd = new JDialog();
						JButton send = new JButton("Send");
						send.setMnemonic('S');
						JButton cancel = new JButton("Cancel");
						cancel.setMnemonic('C');
						jta = new JTextArea(10, 10);
						jta.setText(((ProfileMsg)cmsg).getProfile());
						JScrollPane jsp = new JScrollPane(jta);
						JPanel jp = new JPanel();
						jd.add(jsp, BorderLayout.CENTER);
						jp.add(send);
						jp.add(cancel);
						jd.add(jp, BorderLayout.SOUTH);
						jd.setSize(200,200);
						jd.setTitle("Edit Profile");
						jd.setLocationRelativeTo(null);
						jd.setVisible(true);
						cancel.addActionListener(new ActionListener() {
							public void actionPerformed(ActionEvent ae) { jd.dispose(); }
						});
						send.addActionListener(new ActionListener() {
							public void actionPerformed(ActionEvent ae) {
								ProfileMsg pm = new ProfileMsg(TTClient.ip, TTClient.name, TTClient.pwd, TTClient.key, true, jta.getText());
								pm.setProfile(jta.getText());
								pm.setAction(Actions.PROFILE_UPDATE);
								ServerSender.outQ.add(pm);
								jd.dispose();
							}
						});
						
					}
					// must have not been actually editing, just viewing 
					else {
						if (cmsg.getResponse() == Responses.OK) {
							JDialog jd = new JDialog();
							JPanel jp = new JPanel();
							JEditorPane jep = new JEditorPane();
							jep.setContentType("text/html");
							jep.setEditable(false);
							jep.setText(((ProfileMsg)cmsg).getProfile());
							System.out.println(jep.getText());
							JScrollPane jsp = new JScrollPane(jep);
							//jp.add(jsp);
							//jd.add(jp);
							jd.add(jsp);
							jd.setSize(200,200);
							jd.setTitle("Profile: " + ((ProfileMsg)cmsg).getWantsProfile());
							jd.setLocationRelativeTo(null);
							jd.setVisible(true);
						}
						// whoops, can't do that.
						else {
							JOptionPane.showMessageDialog(null, "Not allowed to view this profile", "Denied!", JOptionPane.ERROR_MESSAGE);
						}
					}
					break;
				case OFFLINE_NOTICE:
					// one of our buddies went offline
					TTClient.blDataStatus.remove(cmsg.getUser());
					TTClient.blDataStatus.put(cmsg.getUser(), 0);
					TTClient.updateBLNeeded = true;
					TTClient.playLogout();
					//gah, broken systems!
					// make sure that if they have a chat, they now know they're offline...
					if (TTClient.pmChats.containsKey(cmsg.getUser())) {
						TTClient.pmChats.get(cmsg.getUser()).setSpecialResponse(2);
					}
					break;
				case ONLINE_NOTICE:
					// one of our buddies came online
					TTClient.blDataStatus.remove(cmsg.getUser());
					TTClient.blDataStatus.put(cmsg.getUser(), 1);
					TTClient.updateBLNeeded = true;
					TTClient.playLogin();
					if (TTClient.pmChats.containsKey(cmsg.getUser())) {
						TTClient.pmChats.get(cmsg.getUser()).setSpecialResponse(0);
					}
					break;
				case WANT_BL:
					// we requested our buddy list
					if (((BLMsg) cmsg).getBuddyList() != null) {
						TTClient.blData = makeStringArray(((BLMsg)cmsg).getBuddyList());
						HashMap<String, Integer> h = new HashMap<String, Integer>();
						
						// hackish method of getting things into the online status table initially
						for (String s : TTClient.blData) {
							h.put(s, 0);
						}
						// now we query the entire group status
						QueryMsg qm = new QueryMsg(TTClient.ip, TTClient.name, TTClient.pwd, TTClient.key, TTClient.name);
						qm.setAction(TTConstants.Actions.QUERY_GROUP);
						qm.setUserList(h);
						ServerSender.outQ.add(qm);
						TTClient.updateBLNeeded = true;
					}
					else {
						// have no buddy list -- just add a blank line or something
						String[] s = { "" };
						TTClient.blData = s;
						TTClient.updateBLNeeded = true;
					}
					break;
				case QUERY_GROUP:
					// we got back a hashtable full of the status of the group
					TTClient.blDataStatus = ((QueryMsg)cmsg).getUserList();
					TTClient.updateBLNeeded = true;
					break;
				case BL_UPDATE:
					// we tried to update our list. complain if things failed
					if (cmsg.getResponse() != Responses.OK) {
						JOptionPane.showMessageDialog(null, "BuddyList update failed!", "Update failed!", JOptionPane.WARNING_MESSAGE);
					}
					else {
						// reset our copy of bl data.
						TTClient.blData = null;
						TTClient.blData = makeStringArray(((BLMsg)cmsg).getBuddyList());
						HashMap<String, Integer> h = new HashMap<String, Integer>();
						for (String s : TTClient.blData) {
							h.put(s, 0);
						}
						// and query for group status again
						QueryMsg qm = new QueryMsg(TTClient.ip, TTClient.name, TTClient.pwd, TTClient.key, TTClient.name);
						qm.setAction(TTConstants.Actions.QUERY_GROUP);
						qm.setUserList(h);
						ServerSender.outQ.add(qm);
						TTClient.updateBLNeeded = true;
					}
					break;
				case CHAT_UPDATE:
					// a group chat update message came in
					// if we do find the chat in the chat table
					//System.out.println("Chat title is " + ((ChatMsg)cmsg).getTitle());
					if (TTClient.chats.containsKey(((ChatMsg)cmsg).getTitle()) && (cmsg.getResponse() == Responses.OK)) {
						// update it
						//System.out.println(((ChatMsg)cmsg).getTitle());
						TTClient.chats.get(((ChatMsg)cmsg).getTitle()).updateChat(cmsg.getUser(), cmsg.getMsg());
						if (!cmsg.getUser().equals(TTClient.name)) TTClient.playMsgIn(); // only chime if it was an external update.
					}
					// otherwise, we just ignore the message, we must not be caring about it anymore or its a mistake
					//else System.out.println("couldnt find chat");
					break;
				case JOIN_CHAT:
					// we tried to join a chat
					if (cmsg.getResponse() == Responses.OK) {
						((ChatMsg)cmsg).getMembers().add(TTClient.name);
						// sweet, create a new one
						TTClient.chats.put(((ChatMsg)cmsg).getTitle(), new Chat((ChatMsg)cmsg));
					}
					else {
						// complain 
						JOptionPane.showMessageDialog(null, "Not allowed to join this chat, or it doesn't exist.", "Join chat failed!", JOptionPane.ERROR_MESSAGE);
					}
					break;
				case NEW_CHAT:
					// we tried to create a chat
					if (cmsg.getResponse() != Responses.DENY) {
						// yay, add it to the list of running group chats!
						TTClient.chats.put(((ChatMsg)cmsg).getTitle(), new Chat((ChatMsg) cmsg));
					}
					else {
						// whine and cry
						JOptionPane.showMessageDialog(null, "Server does not allow you to create this chat.", "Create chat failed!", JOptionPane.ERROR_MESSAGE);
					}
					break;
				}
			}
			else {
				// queue was empty, so sleep as not to absolutely kill CPU/memory usage.
				// YES, this was a real problem until we added this in!
				try { Thread.sleep(100); }
				catch(InterruptedException e) {}
			}
		}
	}
	
	//OH MY GOD I HATE JAVA WHY CAN'T I JUST CAST FROM OBJECT[] TO STRING[] ??
	// this method exists as a convient way to get from an object[] array to a string[] array
	private String[] makeStringArray(ArrayList<String> al) {
		String[] s = new String[al.size()];
		for (int i = 0; i < s.length; i++) {
			s[i] = al.get(i);
			System.out.println(i + " " + s[i]);
		}
		return s;
	}
	
	/**
	 * Kills off this thread/class
	 */
	public static void killMsgParser() {
		while (go) {
			if (ServerListener.inQ.isEmpty()) go = false;
			else try {
				Thread.sleep(10);
			} 
			catch (Exception e) {}
		}
	}
}
